First-pass attempt at characterizing calcium spikes

# install.packages("Rcatch22")
# install.packages("dichromat")
# install.packages("DescTools")

library(Rcatch22)
library(smooth)
Loading required package: greybox
Package "greybox", v2.0.0 loaded.

This is package "smooth", v4.0.0
library(tidyverse)
── Attaching core tidyverse packages ────────────────────────────────────────────────────────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.4.4     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.0
✔ purrr     1.0.2     ── Conflicts ──────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ lubridate::hm() masks greybox::hm()
✖ dplyr::lag()    masks stats::lag()
✖ tidyr::spread() masks greybox::spread()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
source("traceAnalysisFxns.r")

numRuns <- function(x, threshold=0.2,len=2) 
{
    x[x>=threshold]
    out <- rle(x)$lengths
    length(out[out>=len])
}

numSpikes <- function(x, threshold=0.17)
{
    length(which(x>=threshold))
}
avgSpikeLength <- function(x, threshold=0.17) 
{
    pts_above_th <- x[x>=threshold]
    out <- tryCatch({rle(pts_above_th)$lengths},error=function(cond){NA})
    tryCatch({mean(out)},error=function(cond){message(cond);NA})
}

avgTimeBetweenSpikes <- function(x, threshold=0.17)
{
    pts_above_th <- x[x>=threshold]
    out <- tryCatch({inverse.rle(pts_above_th)$lengths},error=function(cond){NA})
    tryCatch({mean(out)},error=function(cond){NA})
}


std_colnames <- c("reg_id","im_idx","time_sec","fura_ratio","expt_date")

fixColnames <- function(x) sapply(x, function(z) 
    switch(z,
           ND.T="im_idx",
           "ROI ID" = "reg_id",
           totalSec = "time_sec",
           Fura_Ratio = "fura_ratio",
           ratio_340_380 = "fura_ratio",
           "Ratio 340/380" = "fura_ratio",
           ROI = "reg_id",
           z))

convertTime <- function(dat) {
    cn <- colnames(dat)
    if("Time_ms" %in% cn & ! "time_sec" %in% cn) {
        dat <- dat %>% separate(Time_ms, c("Minute", "Second"), sep = ":", convert = TRUE) %>% 
            mutate(time_sec = Minute*60 + Second)  %>% select(all_of(setdiff(c(cn,"time_sec"),c("Time_ms","Minute","Second"))))
    } else {
        dat
    }
}

processRawXLSX <- function(fpath, filter_min_area=100) {
    stopifnot(any(grepl("\\.xlsx", fpath)))
    
    expt_date <- substr(sapply(strsplit(fpath, "/"), "[[", 7),1,8)
    cond <- NA

    if(any(grepl("csv", fpath))) cond_temp <- sapply(strsplit(sapply(strsplit(fpath, "/"), "[[", 7), "\\.csv\\?"), "[[", 1)
    if(any(grepl("xlsx", fpath))) cond_temp <- sapply(strsplit(sapply(strsplit(fpath, "/"), "[[", 7), "\\.xlsx\\?"), "[[", 1)

    cond_pt1 <- sapply(strsplit(cond_temp, "_"), "[[", 2)
    cond_pt2 <- sapply(strsplit(cond_temp, "_"), "[[", 3)
    cond_pt3 <- ifelse(length(strsplit(cond_temp, "_")) >= 4, sapply(strsplit(cond_temp, "_"), "[[", 4), "")
    cond_temp <- paste(cond_pt1, cond_pt2, cond_pt3, sep="_")

    if(any(grepl("naive", cond_temp))) {
        naive_temp <- c(cond_pt1,cond_pt2,cond_pt3)[grep("naive",c(cond_pt1,cond_pt2,cond_pt3))]
        cond <- paste(expt_date, naive_temp, sep="_")
    }
    if(any(grepl("untreated", cond_temp))) 
        cond <- paste(expt_date, "naive", sep="_")
    if(any(grepl("PLX",cond_temp))) 
        cond <- paste(expt_date, sapply(strsplit(cond_temp, "_"), "[[", 3), sep="_")
    if(any(grepl("free",cond_temp))) 
        cond <- paste(expt_date, "3day8uMPLXCa2free", sep="_")
    if(any(grepl("Ca2containing",cond_temp))) 
        cond <- paste(expt_date, "3day8uMPLX_w_Ca2", sep="_")
    if(any(grepl("MEK",cond_temp))) 
        cond <- paste(expt_date, "30minMEKi", sep="_")

    if(any(grepl("dropbox",fpath))) {
        temp <- tempfile()
        download.file(fpath, temp, mode="wb")
    } else {
        temp <- fpath
    }
    dat <- readxl::read_xlsx(temp)
    keep_ocols <- c("Time [m:s]","ND.T","ROI ID","Intensity(340)","StDev(340)","Intensity(380)","StDev(380)","ROI Area [µm²]","Ratio 340/380")
    dat <- dat[,keep_ocols]
    times <- unique(dat$`Time [m:s]`)
    diff_times <- difftime(times, times[1], units="secs", tz="UTC")
    dat$time_sec <- DescTools::RoundTo(diff_times[match(dat$`Time [m:s]`,times)], multiple=5)
    # remove objects < 100 µM^2
    dat <- dat[dat$`ROI Area [µm²]` > filter_min_area,]
    
    # fix colnames
    colnames(dat) <- fixColnames(colnames(dat))
    dat$expt_date <- expt_date
    dat$cond <- cond
    return(as.data.frame(dat[,c(std_colnames,"cond")]))
}

preprocessData <- function(fpath) {
    expt_date <- substr(sapply(strsplit(fpath, "/"), "[[", 7),1,8)

    cond_temp <- sapply(strsplit(sapply(strsplit(fpath, "/"), "[[", 7), "\\.csv\\?"), "[[", 1)
    cond_pt1 <- sapply(strsplit(cond_temp, "_"), "[[", 2)
    cond_pt2 <- sapply(strsplit(cond_temp, "_"), "[[", 3)
    cond_pt3 <- sapply(strsplit(cond_temp, "_"), "[[", 4)
    cond_temp <- paste(cond_pt1, cond_pt2, cond_pt3, sep="_")

    if(any(grepl("naive", cond_temp))) 
        cond <- paste(expt_date, "naive", sep="_")
    if(any(grepl("untreated", cond_temp))) 
        cond <- paste(expt_date, "naive", sep="_")
    if(any(grepl("PLX",cond_temp))) 
        cond <- paste(expt_date, sapply(strsplit(cond_temp, "_"), "[[", 3), sep="_")
    if(any(grepl("free",cond_temp))) 
        cond <- paste(expt_date, "8day8uMPLXCa2free", sep="_")
    if(any(grepl("Ca2containing",cond_temp))) 
        cond <- paste(expt_date, "3day8uMPLX_w_Ca2", sep="_")
    if(any(grepl("MEK",cond_temp))) 
        cond <- paste(expt_date, "30minMEKi", sep="_")
    

    d <- read.csv(fpath)
    d <- convertTime(d)
    colnames(d) <- fixColnames(colnames(d))
    d <- d[,intersect(std_colnames,colnames(d))]
    d$expt_date <- expt_date
    d$cond <- cond
    return(d)
}

sampleROIs <- function(condition, ids=rois, n=100) sample(ids[grep(condition,ids)],n)

plotPCcatch <- function(roi, group_ids=clust$cluster, ...) {
    groups <- unique(group_ids)
    mycolors <- dichromat::colorschemes$Categorical.12[seq_along(groups)]
    plot(pc_catch[['x']][,1], pc_catch[['x']][,2], type="n",
         xlab="PC1", ylab="PC2", ...)
    sapply(groups, function(group) points(pc_catch[['x']][which(group_ids==group & rois %in% roi),1], 
                                          pc_catch[['x']][which(group_ids==group & rois %in% roi),2], 
                                          col=mycolors[match(group,groups)],
                                          pch=19, cex=0.5,))
    return(invisible(NULL))
}

Datasets

20220425 = naive
20230426 = untreated
20211114 = tolerant? 3 days?
20220321_fura2_A375H2bRFP_4day8uMPLX 20220321_fura2_A375H2bRFP_5day8uMPLX 20230426_idlingCa2free_pulsing002_V02

fpaths <- c("https://www.dropbox.com/scl/fi/yprt6h4s2khr7sk0kaoav/20230426_untreated_pulsing_mod.csv?rlkey=bt575yvb612cgf1bi23fb9n99&dl=1",
            # "https://www.dropbox.com/scl/fi/q5gxqbib7sjkbnzrjsl2q/20220425_Fura2_naive_pulsing_mod.csv?rlkey=9frhbyh7urxdm8s6h1k8n5vut&dl=1",
            "https://www.dropbox.com/scl/fi/fk0fh1trr46aromfqd31k/20220321_fura2_A375H2bRFP_5day8uMPLX.csv?rlkey=jhhm821t8q63c3n6yb6udasxg&dl=1",
            "https://www.dropbox.com/scl/fi/r0bitvu4ljz19jah9f9p8/20220321_fura2_A375H2bRFP_7day8uMPLX.csv?rlkey=i6wnyo48z5a0v4ovsrgysz2xm&dl=1",
            "https://www.dropbox.com/scl/fi/9j6dvk9vnar3cn5xe24he/20220321_fura2_A375H2bRFP_10day8uMPLX.csv?rlkey=62l5qpih4oetwrte0k3bk3szr&dl=1",
             "https://www.dropbox.com/scl/fi/6pq4d62ser0bcsuvwfcpv/20230426_idlingCa2free_pulsing002_V02_mod.csv?rlkey=k19wxifmfh3rn69zdgn0kb3tc&dl=1"
            )

rawdatapaths <- c(
                  "https://www.dropbox.com/scl/fi/f0ap9tuwfldtrjk2pd3yn/20220425_Fura2_naive002.xlsx?rlkey=dx1zwj6ov8zwzqt944jwc05z9&dl=1",
                  "https://www.dropbox.com/scl/fi/ru1cunsuqgi691ua05dgs/20220425_Fura2_naive003.xlsx?rlkey=f1q7uh454sqjspmr3anabdy1k&dl=1"
                  )

fp <- "https://www.dropbox.com/s/elwck3cnpzvg4uf/20211114_A375_Ca2%2B_pulsing_data.csv?dl=1"

d <- read.csv(fp)
colnames(d) <- c("reg_id", "fura_ratio", "time_sec", "time_min")
d$expt_date <- "20211114"
d$im_idx <- match(d$time_sec, sort(unique(d$time_sec)))
colnames(d) <- fixColnames(colnames(d))
d <- d[,intersect(std_colnames,colnames(d))]
d$cond <- "20211114_14day8uMPLX"

d <- rbind(d, do.call(rbind, lapply(fpaths, preprocessData)))
d <- rbind(d, do.call(rbind, lapply(rawdatapaths, processRawXLSX)))
trying URL 'https://www.dropbox.com/scl/fi/f0ap9tuwfldtrjk2pd3yn/20220425_Fura2_naive002.xlsx?rlkey=dx1zwj6ov8zwzqt944jwc05z9&dl=1'
Content type 'application/binary' length 18257512 bytes (17.4 MB)
==================================================
downloaded 17.4 MB

trying URL 'https://www.dropbox.com/scl/fi/ru1cunsuqgi691ua05dgs/20220425_Fura2_naive003.xlsx?rlkey=f1q7uh454sqjspmr3anabdy1k&dl=1'
Content type 'application/binary' length 16680046 bytes (15.9 MB)
==================================================
downloaded 15.9 MB
# calculate/adjust columns
d$reg_id <- paste(d$cond, d$reg_id, sep="_")
d$intensity_mean <- d$fura_ratio

# ensure all times are a multiple of 5
d$time_sec <- DescTools::RoundTo(d$time_sec, multiple=5)

NOTE

Experiments are each of different duration. To facilitate interpretation, use number of time points of the shortest dataset for each

expt_dates <- unique(d$expt_date)
conditions <- unique(d$cond)

n_im_idx <- min(sapply(conditions, function(co) length(unique((d[d$cond==co,"im_idx"])))))

temp <- lapply(unique(d$cond), function(co) {
    a <- d[d$cond==co,]
    a <- a[a$time_sec %in% tail(unique(a$time_sec), n_im_idx),]
    a$time_sec <- DescTools::RoundTo(a$time_sec - min(a$time_sec), multiple=5)
    # a$time_sec <- seq(0,((n_im_idx-1)*5),5)
    a$im_idx <- match(a$time_sec, sort(unique(a$time_sec)))
    rownames(a) <- NULL
    return(a)
})

d <- do.call(rbind, temp)
rois <- unique(d$reg_id)
times <- unique(d$time_sec)

# fr = fura2_ratio
fr <- data.frame(matrix(d$fura_ratio, ncol=length(rois)))
colnames(fr) <- as.character(rois)

# linear model - intersect
l <- do.call(c, lapply(conditions, function(co) {
    apply(fr[,grepl(co,colnames(fr))], 2, function(x) coef(lm(x ~ times))[2])
}))


# mean fura_ratio
fr_mean <- do.call(cbind,lapply(conditions, function(co) {
    apply(fr[,grepl(co,colnames(fr))], 1, mean)
}))
colnames(fr_mean) <- conditions

# frn = fura2_ratio normalized (mean-centered)
frn <- do.call(cbind, lapply(conditions, function(co) {
    apply(fr[,grepl(co,colnames(fr))], 2, function(x) x - fr_mean[,match(co,conditions)])
}))

d$fura_ratio_norm <- as.vector(frn)
d$int_mean_norm <- as.vector(frn)

Things to consider

  • DTW
  • Sum of time above threshold
  • Stretches of times above threshold
  • Times between spikes
  • Number of spikes
  • Determine the background for each trace

interesting ROIs: 11, 12, 18, 20, 25, 27

checkthese <- paste("20211114_14day8uMPLX", c(11, 12, 18, 20, 25, 27), sep="_")
plot(fura_ratio ~ time_sec, data=d[d$reg_id %in% checkthese,], type="n", ylim=c(0,1))
for(roi in checkthese) lines(fura_ratio ~ time_sec, data=d[d$reg_id==roi,], col=dichromat::colorschemes$Categorical.12[match(roi,checkthese)])

spike_height <- sd(d$fura_ratio_norm)*2.5
roi_list <- lapply(unique(d$reg_id), function(roi) 
    {
        dat <- d[d$reg_id==roi,"fura_ratio_norm"]
        thresh <- .15
        out <- data.frame(reg_id=roi,
                          mean=mean(dat),
                          sd=sd(dat),
                          avg.spike.length=avgSpikeLength(dat, threshold=thresh),
                          num.runs=numRuns(dat, threshold=thresh),
                          num.spikes=numSpikes(dat,threshold=thresh),
                          slope=coef(lm(dat ~ times))[2],
                          intercept=coef(lm(dat ~ times))[1]
                          )
        rownames(out) <- NULL
        return(out)
    })

sumstats <- do.call(rbind, roi_list)
sumstats$avg.spike.length[is.na(sumstats$avg.spike.length)] <- 0

catch <- lapply(unique(d$reg_id), function(roi) 
    {
        dat <- catch22_all(d[d$reg_id==roi,"fura_ratio_norm"])
        dat <- as.data.frame(dat)
        rownames(dat) <- dat[,1]
        dat$names <- NULL
        dat <- t(dat)

        return(dat)
    })
Warning: As of 0.1.14 the feature 'CO_f1ecac' returns a double instead of int
pc_catch <- prcomp(scale(catch))
plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5)

biplot(pc_catch, cex=0.5)

sumstats_scaled <- sumstats
sumstats_scaled[,-1] <- scale(sumstats_scaled[,-1])

lo_signal <- subset(sumstats, mean<0.05 & sd<0.01)
lo_ids <- lo_signal$reg_id
message(paste("There are",nrow(lo_signal),"cells with very low activity"))
There are 1942 cells with very low activity

Catch-22 variables

Location of low-signal traces in Catch-22 PCA space

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5, xlab="PC1", ylab="PC2")
points(pc_catch[['x']][lo_signal$reg_id,1], pc_catch[['x']][lo_signal$reg_id,2], pch=19, cex=0.5, col="yellow")

Boundaries of Catch-22 PCA space

IDs on the boundaries (manually identified from biplot above) 604, 522, 210, 870, 946, 539, 542, 744, 675

eps_val <- 2
clust <- dbscan::dbscan(pc_catch[['x']], eps=eps_val, minPts = 20)

mycols <- rev(dichromat::colorschemes$Categorical.12)[seq(length(unique(clust$cluster)))]

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5, 
     col=mycols[clust$cluster+1],
     xlab="PC1", ylab="PC2")
mtext(paste("eps=",eps_val,"produced",length(unique(clust$cluster)),"clusters"), 3)
legend(-12,6,legend=paste("clust",seq(length(unique(clust$cluster)))), col=mycols, pch=19, cex=0.5)

message(cat("With eps=",eps_val,",",length(unique(clust$cluster)), "clusters were identified"))
With eps= 2 , 2 clusters were identified
diprate::nEach(clust$cluster)
   0    1 
1501 4111 

No spikes looks like cluster==1

set.seed(10)
checkthese <- sample(rois[clust$cluster==1], 15)
plotTraceFura(checkthese, d)

Compare to lo_ids

plotTraceFura(lo_ids[1:15], d)

all_no_spike_ids <- union(rois[clust$cluster==1],lo_ids)
length(all_no_spike_ids)
[1] 4211

others (with spikes?) cluster==0

set.seed(6)
checkthese <- sample(rois[clust$cluster==0], 60)
plotTraceFura(checkthese[1:15], d)

Criteria

Philip described a few basic types of traces he sees:

  1. no spikes
  2. periodic spiking (oscillatory)
  3. sporadic individual spiking (rare and random)
  4. sporadic sustained spiking
  5. sustained activation (two-level trace)

Algorithms for classification

Fast fourier transform to detect regular oscillatory patterns

f <- mvfft(as.matrix(frn))

oscil <- sapply(rois, function(roi) any(Re(f[,as.character(roi)])[3:(nrow(f)-3)] > 10))
roi_oscil <- rois[oscil]
plot(seq(2,nrow(f)-1), tail(head(Re(f[,roi_oscil[2]]),-1),-1), type='l', ylim=c(-1,20))

Example oscillatory traces

plotTraceFura(roi_oscil[11:20], d)

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5, 
     col=c("blue","orange")[(sumstats$reg_id %in% roi_oscil)+1],
     xlab="PC1", ylab="PC2")

Example traces with hi mean values

hi <- sumstats_scaled[sumstats_scaled$mean >=0.1 & sumstats_scaled$sd <= 0,"reg_id"]

plotTraceFura(hi[1:10], d)

Single spikes

single_spike <- sumstats[sumstats$num.spikes==1,"reg_id"]
mycols3 <- viridisLite::viridis(n=38)
plotTraceFura(single_spike[1:20], d)

hi_slope <- rois[which(l>4e-5)]
plotTraceFura(hi_slope[1:10], d)

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5, 
     col=c("blue","orange")[(sumstats$reg_id %in% hi_slope)+1],
     xlab="PC1", ylab="PC2")

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=19, cex=0.5, xlab="PC1", ylab="PC2")
points(pc_catch[['x']][single_spike,1], pc_catch[['x']][single_spike,2], pch=19, cex=0.5, col="yellow")

Expt conditions

plot(pc_catch[['x']][,1], pc_catch[['x']][,2], pch=".", cex=0.5, 
     col=rev(dichromat::colorschemes$Categorical.12)[match(gsub("_[0-9]{1,3}$", "", sumstats$reg_id),conditions)],
     xlab="PC1", ylab="PC2")
legend(-13,8, conditions, col=dichromat::colorschemes$Categorical.12[seq_along(conditions)], cex=0.5, pch=19)

set.seed(13)
my_samples <- invisible(lapply(conditions, function(cond) {
    a <- sampleROIs(condition=cond)
    plotPCcatch(a, clust$cluster, main=cond)
    a
}))

set.seed(13)
my_samples <- invisible(lapply(conditions, function(cond) {
    a <- sampleROIs(condition=cond)
    plotPCcatch(a, as.integer(!rois %in% all_no_spike_ids), main=cond)
    a
}))

lapply(seq_along(my_samples), function(i) plotTraceFura(my_samples[[i]][!my_samples[[i]] %in% all_no_spike_ids][1:20], d))
[[1]]

[[2]]

[[3]]

[[4]]

[[5]]

[[6]]

[[7]]

[[8]]

Example traces

Figure 1B

exemplar_ids <- c(  "20211114_14day8uMPLX_105",
                    "20211114_14day8uMPLX_802",
                    "20211114_14day8uMPLX_297",
                    "20220321_7day8uMPLX_340",
                    "20220321_7day8uMPLX_1121",
                    "20211114_14day8uMPLX_604",
                    "20211114_14day8uMPLX_20"
)

spike_examples <- d[d$reg_id %in% exemplar_ids, ]
spike_examples$reg_id <- factor(spike_examples$reg_id, levels=exemplar_ids)

plotTraceFura(exemplar_ids, spike_examples, yl=c(0.5,1.25))


# ggsave("example_fura2_traces.png", device="png", width=5, height=3.5, units="in")

Plot % in each cluster for each condition

Perform bootstrapping to get sample statistics. Assessing proportions of cells in clusters 0 and 1

# filtered objects in naive data (should filter all datasets) with area > 100 µM^2 

set.seed(13)

prop_spiking <- do.call(c, lapply(conditions, function(co) {
    samp <- lapply(1:100, function(i) sample(rois[grepl(co,rois)],200, replace=TRUE))
    sapply(samp, function(x) length(which(!x %in% all_no_spike_ids))/200)
}))

prop_spiking_df <- data.frame(condition=rep(conditions, each=100), 
                              prop_spiking=prop_spiking)

prop_spiking_cond_df <- prop_spiking_df
prop_spiking_cond_df$date <-  sapply(strsplit(prop_spiking_cond_df$condition, "_"), "[[", 1)
prop_spiking_cond_df$condition <-  sapply(strsplit(prop_spiking_cond_df$condition, "_"), "[[", 2)
prop_spiking_cond_df$condition <-  gsub("day8uMPLX","",prop_spiking_cond_df$condition)
prop_spiking_cond_df[grep("naive",prop_spiking_cond_df$condition),"condition"] <-  0
prop_spiking_cond_df$condition <-  factor(prop_spiking_cond_df$condition, levels=c("0", "5","7","10","14"))
prop_spiking_cond_df <- prop_spiking_cond_df[prop_spiking_cond_df$date != "20230426",]

Time dependence of spiking behavior

Figure 1C

p <- ggplot(data=prop_spiking_cond_df, aes(x=condition, y=prop_spiking, fill=1)) + 
    geom_violin(width=.75) + geom_boxplot(width=0.1, color="grey", alpha=0.5) + 
    # coord_flip() + 
    theme(legend.position="none", 
          axis.text.x = element_text(size=16),
          axis.text.y = element_text(size=16),
          axis.title.x = element_text(size=18, face="bold"),
          axis.title.y = element_text(size=18, face="bold")) + 
    ylim(c(0,0.75)) +
    ylab("Proportion spiking") + 
    xlab("Days in BRAFi")
p


# ggsave("BRAFi_time_prop_spiking.png", device="png", width=4, height=4, units="in")
m <- lm(prop_spiking ~ condition, data=prop_spiking_cond_df)
a <- aov(m)
tukey <- TukeyHSD(a)
tukey$condition
          diff          lwr        upr        p adj
5-0   0.075075  0.065782529 0.08436747 4.747650e-10
7-0   0.204875  0.195582529 0.21416747 4.747650e-10
10-0  0.209325  0.200032529 0.21861747 4.747650e-10
14-0  0.531025  0.521732529 0.54031747 4.747650e-10
7-5   0.129800  0.119069979 0.14053002 4.747650e-10
10-5  0.134250  0.123519979 0.14498002 4.747650e-10
14-5  0.455950  0.445219979 0.46668002 4.747650e-10
10-7  0.004450 -0.006280021 0.01518002 7.880375e-01
14-7  0.326150  0.315419979 0.33688002 4.747650e-10
14-10 0.321700  0.310969979 0.33243002 4.747650e-10

Number of cells per condition

sapply(conditions, function(co) length(unique(rois[grep(co,rois)])))
      20211114_14day8uMPLX             20230426_naive        20220321_5day8uMPLX        20220321_7day8uMPLX       20220321_10day8uMPLX 
                       882                        224                       1202                       1141                       1152 
20230426_8day8uMPLXCa2free          20220425_naive002          20220425_naive003 
                       266                        383                        362 

Analysis of extracellular calcium on long-term BRAFi-treated cell spiking behavior

Supplementary Figure S4

Analysis of manual quantification of 8 day BRAFi w/o calcium

Performed by Philip. Will append manually assessed no-spike reg_ids to all_no_spike_ids.

noCa_8d_manual <- read.csv("../data/20230426_8dBRAFica2free_sample_ids.csv")
testrois <- c(noCa_8d_manual$reg_id, rois[grepl("20220321_7day8uMPLX",rois)])
set.seed(13)
testconds <- c("20220321_7day8uMPLX","20230426_8day8uMPLXCa2free")

prop_spiking_ca <- do.call(c, lapply(testconds, function(co) {
    samp <- lapply(1:100, function(i) sample(testrois[grepl(co,testrois)], 200, replace = TRUE))
    sapply(samp, function(x) length(which(!x %in% c(all_no_spike_ids,noCa_8d_manual$reg_id[noCa_8d_manual$spikes==0])))/200)
}))

prop_spiking_ca_df <- data.frame(condition=rep(c("Ca2+","no Ca2+"), each=100),
                                 prop_spiking=prop_spiking_ca)

8 days BRAFi w/o Ca2+ in buffer vs 7 days BRAFi w/ Ca2+ in buffer

Supplementary Figure S4

p <- ggplot(data=prop_spiking_ca_df, aes(x=condition, y=prop_spiking, fill=1)) + 
    geom_violin(width=.75) + 
    geom_boxplot(width=0.1, color="grey", alpha=0.5) + 
    # coord_flip() +
    theme(legend.position="none", 
          axis.text.x = element_text(size=12),
          axis.text.y = element_text(size=12),
          axis.title.x = element_text(size=16, face="bold"),
          axis.title.y = element_text(size=16, face="bold")) + 
    # ylim(c(0,0.75)) +
    ylab("Proportion spiking") + 
    xlab("") 
p

LS0tCnRpdGxlOiAiQ2FjbGl1bSBwdWxzaW5nIGFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZGF0ZTogMjAyMy0wNC0yMwphdXRob3I6IERhcnJlbiBUeXNvbiAmIFBoaWxpcCBTdGF1ZmZlcgotLS0KIyMgRmlyc3QtcGFzcyBhdHRlbXB0IGF0IGNoYXJhY3Rlcml6aW5nIGNhbGNpdW0gc3Bpa2VzCgpgYGB7ciBMb2FkIHBhY2thZ2VzIGFuZCBmdW5jdGlvbnN9CiMgaW5zdGFsbC5wYWNrYWdlcygiUmNhdGNoMjIiKQojIGluc3RhbGwucGFja2FnZXMoImRpY2hyb21hdCIpCiMgaW5zdGFsbC5wYWNrYWdlcygiRGVzY1Rvb2xzIikKCmxpYnJhcnkoUmNhdGNoMjIpCmxpYnJhcnkoc21vb3RoKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKc291cmNlKCJ0cmFjZUFuYWx5c2lzRnhucy5yIikKCm51bVJ1bnMgPC0gZnVuY3Rpb24oeCwgdGhyZXNob2xkPTAuMixsZW49MikgCnsKICAgIHhbeD49dGhyZXNob2xkXQogICAgb3V0IDwtIHJsZSh4KSRsZW5ndGhzCiAgICBsZW5ndGgob3V0W291dD49bGVuXSkKfQoKbnVtU3Bpa2VzIDwtIGZ1bmN0aW9uKHgsIHRocmVzaG9sZD0wLjE3KQp7CiAgICBsZW5ndGgod2hpY2goeD49dGhyZXNob2xkKSkKfQphdmdTcGlrZUxlbmd0aCA8LSBmdW5jdGlvbih4LCB0aHJlc2hvbGQ9MC4xNykgCnsKICAgIHB0c19hYm92ZV90aCA8LSB4W3g+PXRocmVzaG9sZF0KICAgIG91dCA8LSB0cnlDYXRjaCh7cmxlKHB0c19hYm92ZV90aCkkbGVuZ3Roc30sZXJyb3I9ZnVuY3Rpb24oY29uZCl7TkF9KQogICAgdHJ5Q2F0Y2goe21lYW4ob3V0KX0sZXJyb3I9ZnVuY3Rpb24oY29uZCl7bWVzc2FnZShjb25kKTtOQX0pCn0KCmF2Z1RpbWVCZXR3ZWVuU3Bpa2VzIDwtIGZ1bmN0aW9uKHgsIHRocmVzaG9sZD0wLjE3KQp7CiAgICBwdHNfYWJvdmVfdGggPC0geFt4Pj10aHJlc2hvbGRdCiAgICBvdXQgPC0gdHJ5Q2F0Y2goe2ludmVyc2UucmxlKHB0c19hYm92ZV90aCkkbGVuZ3Roc30sZXJyb3I9ZnVuY3Rpb24oY29uZCl7TkF9KQogICAgdHJ5Q2F0Y2goe21lYW4ob3V0KX0sZXJyb3I9ZnVuY3Rpb24oY29uZCl7TkF9KQp9CgoKc3RkX2NvbG5hbWVzIDwtIGMoInJlZ19pZCIsImltX2lkeCIsInRpbWVfc2VjIiwiZnVyYV9yYXRpbyIsImV4cHRfZGF0ZSIpCgpmaXhDb2xuYW1lcyA8LSBmdW5jdGlvbih4KSBzYXBwbHkoeCwgZnVuY3Rpb24oeikgCiAgICBzd2l0Y2goeiwKICAgICAgICAgICBORC5UPSJpbV9pZHgiLAogICAgICAgICAgICJST0kgSUQiID0gInJlZ19pZCIsCiAgICAgICAgICAgdG90YWxTZWMgPSAidGltZV9zZWMiLAogICAgICAgICAgIEZ1cmFfUmF0aW8gPSAiZnVyYV9yYXRpbyIsCiAgICAgICAgICAgcmF0aW9fMzQwXzM4MCA9ICJmdXJhX3JhdGlvIiwKICAgICAgICAgICAiUmF0aW8gMzQwLzM4MCIgPSAiZnVyYV9yYXRpbyIsCiAgICAgICAgICAgUk9JID0gInJlZ19pZCIsCiAgICAgICAgICAgeikpCgpjb252ZXJ0VGltZSA8LSBmdW5jdGlvbihkYXQpIHsKICAgIGNuIDwtIGNvbG5hbWVzKGRhdCkKICAgIGlmKCJUaW1lX21zIiAlaW4lIGNuICYgISAidGltZV9zZWMiICVpbiUgY24pIHsKICAgICAgICBkYXQgPC0gZGF0ICU+JSBzZXBhcmF0ZShUaW1lX21zLCBjKCJNaW51dGUiLCAiU2Vjb25kIiksIHNlcCA9ICI6IiwgY29udmVydCA9IFRSVUUpICU+JSAKICAgICAgICAgICAgbXV0YXRlKHRpbWVfc2VjID0gTWludXRlKjYwICsgU2Vjb25kKSAgJT4lIHNlbGVjdChhbGxfb2Yoc2V0ZGlmZihjKGNuLCJ0aW1lX3NlYyIpLGMoIlRpbWVfbXMiLCJNaW51dGUiLCJTZWNvbmQiKSkpKQogICAgfSBlbHNlIHsKICAgICAgICBkYXQKICAgIH0KfQoKcHJvY2Vzc1Jhd1hMU1ggPC0gZnVuY3Rpb24oZnBhdGgsIGZpbHRlcl9taW5fYXJlYT0xMDApIHsKICAgIHN0b3BpZm5vdChhbnkoZ3JlcGwoIlxcLnhsc3giLCBmcGF0aCkpKQogICAgCiAgICBleHB0X2RhdGUgPC0gc3Vic3RyKHNhcHBseShzdHJzcGxpdChmcGF0aCwgIi8iKSwgIltbIiwgNyksMSw4KQogICAgY29uZCA8LSBOQQoKICAgIGlmKGFueShncmVwbCgiY3N2IiwgZnBhdGgpKSkgY29uZF90ZW1wIDwtIHNhcHBseShzdHJzcGxpdChzYXBwbHkoc3Ryc3BsaXQoZnBhdGgsICIvIiksICJbWyIsIDcpLCAiXFwuY3N2XFw/IiksICJbWyIsIDEpCiAgICBpZihhbnkoZ3JlcGwoInhsc3giLCBmcGF0aCkpKSBjb25kX3RlbXAgPC0gc2FwcGx5KHN0cnNwbGl0KHNhcHBseShzdHJzcGxpdChmcGF0aCwgIi8iKSwgIltbIiwgNyksICJcXC54bHN4XFw/IiksICJbWyIsIDEpCgogICAgY29uZF9wdDEgPC0gc2FwcGx5KHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSwgIltbIiwgMikKICAgIGNvbmRfcHQyIDwtIHNhcHBseShzdHJzcGxpdChjb25kX3RlbXAsICJfIiksICJbWyIsIDMpCiAgICBjb25kX3B0MyA8LSBpZmVsc2UobGVuZ3RoKHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSkgPj0gNCwgc2FwcGx5KHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSwgIltbIiwgNCksICIiKQogICAgY29uZF90ZW1wIDwtIHBhc3RlKGNvbmRfcHQxLCBjb25kX3B0MiwgY29uZF9wdDMsIHNlcD0iXyIpCgogICAgaWYoYW55KGdyZXBsKCJuYWl2ZSIsIGNvbmRfdGVtcCkpKSB7CiAgICAgICAgbmFpdmVfdGVtcCA8LSBjKGNvbmRfcHQxLGNvbmRfcHQyLGNvbmRfcHQzKVtncmVwKCJuYWl2ZSIsYyhjb25kX3B0MSxjb25kX3B0Mixjb25kX3B0MykpXQogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCBuYWl2ZV90ZW1wLCBzZXA9Il8iKQogICAgfQogICAgaWYoYW55KGdyZXBsKCJ1bnRyZWF0ZWQiLCBjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICJuYWl2ZSIsIHNlcD0iXyIpCiAgICBpZihhbnkoZ3JlcGwoIlBMWCIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCBzYXBwbHkoc3Ryc3BsaXQoY29uZF90ZW1wLCAiXyIpLCAiW1siLCAzKSwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiZnJlZSIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCAiM2RheTh1TVBMWENhMmZyZWUiLCBzZXA9Il8iKQogICAgaWYoYW55KGdyZXBsKCJDYTJjb250YWluaW5nIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzZGF5OHVNUExYX3dfQ2EyIiwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiTUVLIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzMG1pbk1FS2kiLCBzZXA9Il8iKQoKICAgIGlmKGFueShncmVwbCgiZHJvcGJveCIsZnBhdGgpKSkgewogICAgICAgIHRlbXAgPC0gdGVtcGZpbGUoKQogICAgICAgIGRvd25sb2FkLmZpbGUoZnBhdGgsIHRlbXAsIG1vZGU9IndiIikKICAgIH0gZWxzZSB7CiAgICAgICAgdGVtcCA8LSBmcGF0aAogICAgfQogICAgZGF0IDwtIHJlYWR4bDo6cmVhZF94bHN4KHRlbXApCiAgICBrZWVwX29jb2xzIDwtIGMoIlRpbWUgW206c10iLCJORC5UIiwiUk9JIElEIiwiSW50ZW5zaXR5KDM0MCkiLCJTdERldigzNDApIiwiSW50ZW5zaXR5KDM4MCkiLCJTdERldigzODApIiwiUk9JIEFyZWEgW8K1bcKyXSIsIlJhdGlvIDM0MC8zODAiKQogICAgZGF0IDwtIGRhdFssa2VlcF9vY29sc10KICAgIHRpbWVzIDwtIHVuaXF1ZShkYXQkYFRpbWUgW206c11gKQogICAgZGlmZl90aW1lcyA8LSBkaWZmdGltZSh0aW1lcywgdGltZXNbMV0sIHVuaXRzPSJzZWNzIiwgdHo9IlVUQyIpCiAgICBkYXQkdGltZV9zZWMgPC0gRGVzY1Rvb2xzOjpSb3VuZFRvKGRpZmZfdGltZXNbbWF0Y2goZGF0JGBUaW1lIFttOnNdYCx0aW1lcyldLCBtdWx0aXBsZT01KQogICAgIyByZW1vdmUgb2JqZWN0cyA8IDEwMCDCtU1eMgogICAgZGF0IDwtIGRhdFtkYXQkYFJPSSBBcmVhIFvCtW3Csl1gID4gZmlsdGVyX21pbl9hcmVhLF0KICAgIAogICAgIyBmaXggY29sbmFtZXMKICAgIGNvbG5hbWVzKGRhdCkgPC0gZml4Q29sbmFtZXMoY29sbmFtZXMoZGF0KSkKICAgIGRhdCRleHB0X2RhdGUgPC0gZXhwdF9kYXRlCiAgICBkYXQkY29uZCA8LSBjb25kCiAgICByZXR1cm4oYXMuZGF0YS5mcmFtZShkYXRbLGMoc3RkX2NvbG5hbWVzLCJjb25kIildKSkKfQoKcHJlcHJvY2Vzc0RhdGEgPC0gZnVuY3Rpb24oZnBhdGgpIHsKICAgIGV4cHRfZGF0ZSA8LSBzdWJzdHIoc2FwcGx5KHN0cnNwbGl0KGZwYXRoLCAiLyIpLCAiW1siLCA3KSwxLDgpCgogICAgY29uZF90ZW1wIDwtIHNhcHBseShzdHJzcGxpdChzYXBwbHkoc3Ryc3BsaXQoZnBhdGgsICIvIiksICJbWyIsIDcpLCAiXFwuY3N2XFw/IiksICJbWyIsIDEpCiAgICBjb25kX3B0MSA8LSBzYXBwbHkoc3Ryc3BsaXQoY29uZF90ZW1wLCAiXyIpLCAiW1siLCAyKQogICAgY29uZF9wdDIgPC0gc2FwcGx5KHN0cnNwbGl0KGNvbmRfdGVtcCwgIl8iKSwgIltbIiwgMykKICAgIGNvbmRfcHQzIDwtIHNhcHBseShzdHJzcGxpdChjb25kX3RlbXAsICJfIiksICJbWyIsIDQpCiAgICBjb25kX3RlbXAgPC0gcGFzdGUoY29uZF9wdDEsIGNvbmRfcHQyLCBjb25kX3B0Mywgc2VwPSJfIikKCiAgICBpZihhbnkoZ3JlcGwoIm5haXZlIiwgY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCAibmFpdmUiLCBzZXA9Il8iKQogICAgaWYoYW55KGdyZXBsKCJ1bnRyZWF0ZWQiLCBjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICJuYWl2ZSIsIHNlcD0iXyIpCiAgICBpZihhbnkoZ3JlcGwoIlBMWCIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCBzYXBwbHkoc3Ryc3BsaXQoY29uZF90ZW1wLCAiXyIpLCAiW1siLCAzKSwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiZnJlZSIsY29uZF90ZW1wKSkpIAogICAgICAgIGNvbmQgPC0gcGFzdGUoZXhwdF9kYXRlLCAiOGRheTh1TVBMWENhMmZyZWUiLCBzZXA9Il8iKQogICAgaWYoYW55KGdyZXBsKCJDYTJjb250YWluaW5nIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzZGF5OHVNUExYX3dfQ2EyIiwgc2VwPSJfIikKICAgIGlmKGFueShncmVwbCgiTUVLIixjb25kX3RlbXApKSkgCiAgICAgICAgY29uZCA8LSBwYXN0ZShleHB0X2RhdGUsICIzMG1pbk1FS2kiLCBzZXA9Il8iKQogICAgCgogICAgZCA8LSByZWFkLmNzdihmcGF0aCkKICAgIGQgPC0gY29udmVydFRpbWUoZCkKICAgIGNvbG5hbWVzKGQpIDwtIGZpeENvbG5hbWVzKGNvbG5hbWVzKGQpKQogICAgZCA8LSBkWyxpbnRlcnNlY3Qoc3RkX2NvbG5hbWVzLGNvbG5hbWVzKGQpKV0KICAgIGQkZXhwdF9kYXRlIDwtIGV4cHRfZGF0ZQogICAgZCRjb25kIDwtIGNvbmQKICAgIHJldHVybihkKQp9CgpzYW1wbGVST0lzIDwtIGZ1bmN0aW9uKGNvbmRpdGlvbiwgaWRzPXJvaXMsIG49MTAwKSBzYW1wbGUoaWRzW2dyZXAoY29uZGl0aW9uLGlkcyldLG4pCgpwbG90UENjYXRjaCA8LSBmdW5jdGlvbihyb2ksIGdyb3VwX2lkcz1jbHVzdCRjbHVzdGVyLCAuLi4pIHsKICAgIGdyb3VwcyA8LSB1bmlxdWUoZ3JvdXBfaWRzKQogICAgbXljb2xvcnMgPC0gZGljaHJvbWF0Ojpjb2xvcnNjaGVtZXMkQ2F0ZWdvcmljYWwuMTJbc2VxX2Fsb25nKGdyb3VwcyldCiAgICBwbG90KHBjX2NhdGNoW1sneCddXVssMV0sIHBjX2NhdGNoW1sneCddXVssMl0sIHR5cGU9Im4iLAogICAgICAgICB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiLCAuLi4pCiAgICBzYXBwbHkoZ3JvdXBzLCBmdW5jdGlvbihncm91cCkgcG9pbnRzKHBjX2NhdGNoW1sneCddXVt3aGljaChncm91cF9pZHM9PWdyb3VwICYgcm9pcyAlaW4lIHJvaSksMV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwY19jYXRjaFtbJ3gnXV1bd2hpY2goZ3JvdXBfaWRzPT1ncm91cCAmIHJvaXMgJWluJSByb2kpLDJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPW15Y29sb3JzW21hdGNoKGdyb3VwLGdyb3VwcyldLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwY2g9MTksIGNleD0wLjUsKSkKICAgIHJldHVybihpbnZpc2libGUoTlVMTCkpCn0KCmBgYAojIyMgRGF0YXNldHMKMjAyMjA0MjUgPSBuYWl2ZSAgCjIwMjMwNDI2ID0gdW50cmVhdGVkICAKMjAyMTExMTQgPSB0b2xlcmFudD8gMyBkYXlzPyAgCjIwMjIwMzIxX2Z1cmEyX0EzNzVIMmJSRlBfNGRheTh1TVBMWAoyMDIyMDMyMV9mdXJhMl9BMzc1SDJiUkZQXzVkYXk4dU1QTFgKMjAyMzA0MjZfaWRsaW5nQ2EyZnJlZV9wdWxzaW5nMDAyX1YwMgoKYGBge3IgTG9hZCBhbmQgY29tcGlsZSBkYXRhfQpmcGF0aHMgPC0gYygiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpL3lwcnQ2aDRzMmtocjdzazBrYW9hdi8yMDIzMDQyNl91bnRyZWF0ZWRfcHVsc2luZ19tb2QuY3N2P3Jsa2V5PWJ0NTc1eXZiNjEyY2dmMWJpMjNmYjluOTkmZGw9MSIsCiAgICAgICAgICAgICJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zY2wvZmkvZmswZmgxdHJyNDZhcm9tZnFkMzFrLzIwMjIwMzIxX2Z1cmEyX0EzNzVIMmJSRlBfNWRheTh1TVBMWC5jc3Y/cmxrZXk9amhobTgyMXQ4cTYzYzNuNnliNnVkYXN4ZyZkbD0xIiwKICAgICAgICAgICAgImh0dHBzOi8vd3d3LmRyb3Bib3guY29tL3NjbC9maS9yMGJpdHZ1NGxqejE5amFoOWY5cDgvMjAyMjAzMjFfZnVyYTJfQTM3NUgyYlJGUF83ZGF5OHVNUExYLmNzdj9ybGtleT1pNndueW80OHo1YTB2NG92c3JneXN6MnhtJmRsPTEiLAogICAgICAgICAgICAiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpLzlqNmR2azl2bmFyM2NuNXhlMjRoZS8yMDIyMDMyMV9mdXJhMl9BMzc1SDJiUkZQXzEwZGF5OHVNUExYLmNzdj9ybGtleT02Mmw1cXBpaDRvZXR3cnRlMGszYmszc3pyJmRsPTEiLAogICAgICAgICAgICAiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpLzZwcTRkNjJzZXIwYmNzdXZ3ZmNwdi8yMDIzMDQyNl9pZGxpbmdDYTJmcmVlX3B1bHNpbmcwMDJfVjAyX21vZC5jc3Y/cmxrZXk9azE5d3hpZm1maDNybjY5emRnbjBrYjN0YyZkbD0xIgogICAgICAgICAgICApCgpyYXdkYXRhcGF0aHMgPC0gYygiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vc2NsL2ZpL2YwYXA5dHV3ZmxkdHJqazJwZDN5bi8yMDIyMDQyNV9GdXJhMl9uYWl2ZTAwMi54bHN4P3Jsa2V5PWR4MXp3ajZvdjh6d3pxdDk0NGp3YzA1ejkmZGw9MSIsCiAgICAgICAgICAgICAgICAgICJodHRwczovL3d3dy5kcm9wYm94LmNvbS9zY2wvZmkvcnUxY3Vuc3VxZ2k2OTF1YTA1ZGdzLzIwMjIwNDI1X0Z1cmEyX25haXZlMDAzLnhsc3g/cmxrZXk9ZjFxN3VoNDU0c3Fqc3BtcjNhbmFiZHkxayZkbD0xIgogICAgICAgICAgICAgICAgICApCgpmcCA8LSAiaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy9lbHdjazNjbnB6dmc0dWYvMjAyMTExMTRfQTM3NV9DYTIlMkJfcHVsc2luZ19kYXRhLmNzdj9kbD0xIgoKZCA8LSByZWFkLmNzdihmcCkKY29sbmFtZXMoZCkgPC0gYygicmVnX2lkIiwgImZ1cmFfcmF0aW8iLCAidGltZV9zZWMiLCAidGltZV9taW4iKQpkJGV4cHRfZGF0ZSA8LSAiMjAyMTExMTQiCmQkaW1faWR4IDwtIG1hdGNoKGQkdGltZV9zZWMsIHNvcnQodW5pcXVlKGQkdGltZV9zZWMpKSkKY29sbmFtZXMoZCkgPC0gZml4Q29sbmFtZXMoY29sbmFtZXMoZCkpCmQgPC0gZFssaW50ZXJzZWN0KHN0ZF9jb2xuYW1lcyxjb2xuYW1lcyhkKSldCmQkY29uZCA8LSAiMjAyMTExMTRfMTRkYXk4dU1QTFgiCgpkIDwtIHJiaW5kKGQsIGRvLmNhbGwocmJpbmQsIGxhcHBseShmcGF0aHMsIHByZXByb2Nlc3NEYXRhKSkpCmQgPC0gcmJpbmQoZCwgZG8uY2FsbChyYmluZCwgbGFwcGx5KHJhd2RhdGFwYXRocywgcHJvY2Vzc1Jhd1hMU1gpKSkKCiMgY2FsY3VsYXRlL2FkanVzdCBjb2x1bW5zCmQkcmVnX2lkIDwtIHBhc3RlKGQkY29uZCwgZCRyZWdfaWQsIHNlcD0iXyIpCmQkaW50ZW5zaXR5X21lYW4gPC0gZCRmdXJhX3JhdGlvCgojIGVuc3VyZSBhbGwgdGltZXMgYXJlIGEgbXVsdGlwbGUgb2YgNQpkJHRpbWVfc2VjIDwtIERlc2NUb29sczo6Um91bmRUbyhkJHRpbWVfc2VjLCBtdWx0aXBsZT01KQpgYGAKCgoKIyMjIE5PVEUKRXhwZXJpbWVudHMgYXJlIGVhY2ggb2YgZGlmZmVyZW50IGR1cmF0aW9uLiBUbyBmYWNpbGl0YXRlIGludGVycHJldGF0aW9uLCB1c2UgbnVtYmVyIG9mIHRpbWUgcG9pbnRzIG9mIHRoZSBzaG9ydGVzdCBkYXRhc2V0IGZvciBlYWNoCgpgYGB7cn0KZXhwdF9kYXRlcyA8LSB1bmlxdWUoZCRleHB0X2RhdGUpCmNvbmRpdGlvbnMgPC0gdW5pcXVlKGQkY29uZCkKCm5faW1faWR4IDwtIG1pbihzYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY28pIGxlbmd0aCh1bmlxdWUoKGRbZCRjb25kPT1jbywiaW1faWR4Il0pKSkpKQoKdGVtcCA8LSBsYXBwbHkodW5pcXVlKGQkY29uZCksIGZ1bmN0aW9uKGNvKSB7CiAgICBhIDwtIGRbZCRjb25kPT1jbyxdCiAgICBhIDwtIGFbYSR0aW1lX3NlYyAlaW4lIHRhaWwodW5pcXVlKGEkdGltZV9zZWMpLCBuX2ltX2lkeCksXQogICAgYSR0aW1lX3NlYyA8LSBEZXNjVG9vbHM6OlJvdW5kVG8oYSR0aW1lX3NlYyAtIG1pbihhJHRpbWVfc2VjKSwgbXVsdGlwbGU9NSkKICAgICMgYSR0aW1lX3NlYyA8LSBzZXEoMCwoKG5faW1faWR4LTEpKjUpLDUpCiAgICBhJGltX2lkeCA8LSBtYXRjaChhJHRpbWVfc2VjLCBzb3J0KHVuaXF1ZShhJHRpbWVfc2VjKSkpCiAgICByb3duYW1lcyhhKSA8LSBOVUxMCiAgICByZXR1cm4oYSkKfSkKCmQgPC0gZG8uY2FsbChyYmluZCwgdGVtcCkKYGBgCgoKYGBge3J9CnJvaXMgPC0gdW5pcXVlKGQkcmVnX2lkKQp0aW1lcyA8LSB1bmlxdWUoZCR0aW1lX3NlYykKCiMgZnIgPSBmdXJhMl9yYXRpbwpmciA8LSBkYXRhLmZyYW1lKG1hdHJpeChkJGZ1cmFfcmF0aW8sIG5jb2w9bGVuZ3RoKHJvaXMpKSkKY29sbmFtZXMoZnIpIDwtIGFzLmNoYXJhY3Rlcihyb2lzKQoKIyBsaW5lYXIgbW9kZWwgLSBpbnRlcnNlY3QKbCA8LSBkby5jYWxsKGMsIGxhcHBseShjb25kaXRpb25zLCBmdW5jdGlvbihjbykgewogICAgYXBwbHkoZnJbLGdyZXBsKGNvLGNvbG5hbWVzKGZyKSldLCAyLCBmdW5jdGlvbih4KSBjb2VmKGxtKHggfiB0aW1lcykpWzJdKQp9KSkKCgojIG1lYW4gZnVyYV9yYXRpbwpmcl9tZWFuIDwtIGRvLmNhbGwoY2JpbmQsbGFwcGx5KGNvbmRpdGlvbnMsIGZ1bmN0aW9uKGNvKSB7CiAgICBhcHBseShmclssZ3JlcGwoY28sY29sbmFtZXMoZnIpKV0sIDEsIG1lYW4pCn0pKQpjb2xuYW1lcyhmcl9tZWFuKSA8LSBjb25kaXRpb25zCgojIGZybiA9IGZ1cmEyX3JhdGlvIG5vcm1hbGl6ZWQgKG1lYW4tY2VudGVyZWQpCmZybiA8LSBkby5jYWxsKGNiaW5kLCBsYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY28pIHsKICAgIGFwcGx5KGZyWyxncmVwbChjbyxjb2xuYW1lcyhmcikpXSwgMiwgZnVuY3Rpb24oeCkgeCAtIGZyX21lYW5bLG1hdGNoKGNvLGNvbmRpdGlvbnMpXSkKfSkpCgpkJGZ1cmFfcmF0aW9fbm9ybSA8LSBhcy52ZWN0b3IoZnJuKQpkJGludF9tZWFuX25vcm0gPC0gYXMudmVjdG9yKGZybikKYGBgCgoKIyMjIFRoaW5ncyB0byBjb25zaWRlcgoqIERUVyAgCiogU3VtIG9mIHRpbWUgYWJvdmUgdGhyZXNob2xkICAKKiBTdHJldGNoZXMgb2YgdGltZXMgYWJvdmUgdGhyZXNob2xkCiogVGltZXMgYmV0d2VlbiBzcGlrZXMKKiBOdW1iZXIgb2Ygc3Bpa2VzCiogRGV0ZXJtaW5lIHRoZSBiYWNrZ3JvdW5kIGZvciBlYWNoIHRyYWNlCgppbnRlcmVzdGluZyBST0lzOiAxMSwgMTIsIDE4LCAyMCwgMjUsIDI3CmBgYHtyfQpjaGVja3RoZXNlIDwtIHBhc3RlKCIyMDIxMTExNF8xNGRheTh1TVBMWCIsIGMoMTEsIDEyLCAxOCwgMjAsIDI1LCAyNyksIHNlcD0iXyIpCnBsb3QoZnVyYV9yYXRpbyB+IHRpbWVfc2VjLCBkYXRhPWRbZCRyZWdfaWQgJWluJSBjaGVja3RoZXNlLF0sIHR5cGU9Im4iLCB5bGltPWMoMCwxKSkKZm9yKHJvaSBpbiBjaGVja3RoZXNlKSBsaW5lcyhmdXJhX3JhdGlvIH4gdGltZV9zZWMsIGRhdGE9ZFtkJHJlZ19pZD09cm9pLF0sIGNvbD1kaWNocm9tYXQ6OmNvbG9yc2NoZW1lcyRDYXRlZ29yaWNhbC4xMlttYXRjaChyb2ksY2hlY2t0aGVzZSldKQpgYGAKCmBgYHtyfQpzcGlrZV9oZWlnaHQgPC0gc2QoZCRmdXJhX3JhdGlvX25vcm0pKjIuNQpyb2lfbGlzdCA8LSBsYXBwbHkodW5pcXVlKGQkcmVnX2lkKSwgZnVuY3Rpb24ocm9pKSAKICAgIHsKICAgICAgICBkYXQgPC0gZFtkJHJlZ19pZD09cm9pLCJmdXJhX3JhdGlvX25vcm0iXQogICAgICAgIHRocmVzaCA8LSAuMTUKICAgICAgICBvdXQgPC0gZGF0YS5mcmFtZShyZWdfaWQ9cm9pLAogICAgICAgICAgICAgICAgICAgICAgICAgIG1lYW49bWVhbihkYXQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNkPXNkKGRhdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgYXZnLnNwaWtlLmxlbmd0aD1hdmdTcGlrZUxlbmd0aChkYXQsIHRocmVzaG9sZD10aHJlc2gpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG51bS5ydW5zPW51bVJ1bnMoZGF0LCB0aHJlc2hvbGQ9dGhyZXNoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBudW0uc3Bpa2VzPW51bVNwaWtlcyhkYXQsdGhyZXNob2xkPXRocmVzaCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2xvcGU9Y29lZihsbShkYXQgfiB0aW1lcykpWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgIGludGVyY2VwdD1jb2VmKGxtKGRhdCB+IHRpbWVzKSlbMV0KICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgcm93bmFtZXMob3V0KSA8LSBOVUxMCiAgICAgICAgcmV0dXJuKG91dCkKICAgIH0pCgpzdW1zdGF0cyA8LSBkby5jYWxsKHJiaW5kLCByb2lfbGlzdCkKc3Vtc3RhdHMkYXZnLnNwaWtlLmxlbmd0aFtpcy5uYShzdW1zdGF0cyRhdmcuc3Bpa2UubGVuZ3RoKV0gPC0gMAoKY2F0Y2ggPC0gbGFwcGx5KHVuaXF1ZShkJHJlZ19pZCksIGZ1bmN0aW9uKHJvaSkgCiAgICB7CiAgICAgICAgZGF0IDwtIGNhdGNoMjJfYWxsKGRbZCRyZWdfaWQ9PXJvaSwiZnVyYV9yYXRpb19ub3JtIl0pCiAgICAgICAgZGF0IDwtIGFzLmRhdGEuZnJhbWUoZGF0KQogICAgICAgIHJvd25hbWVzKGRhdCkgPC0gZGF0WywxXQogICAgICAgIGRhdCRuYW1lcyA8LSBOVUxMCiAgICAgICAgZGF0IDwtIHQoZGF0KQoKICAgICAgICByZXR1cm4oZGF0KQogICAgfSkKCmNhdGNoIDwtIGRvLmNhbGwocmJpbmQsIGNhdGNoKQpyb3duYW1lcyhjYXRjaCkgPC0gdW5pcXVlKGQkcmVnX2lkKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CnBjX2NhdGNoIDwtIHByY29tcChzY2FsZShjYXRjaCkpCnBsb3QocGNfY2F0Y2hbWyd4J11dWywxXSwgcGNfY2F0Y2hbWyd4J11dWywyXSwgcGNoPTE5LCBjZXg9MC41KQpgYGAKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0KYmlwbG90KHBjX2NhdGNoLCBjZXg9MC41KQpgYGAKCgoKYGBge3J9CnN1bXN0YXRzX3NjYWxlZCA8LSBzdW1zdGF0cwpzdW1zdGF0c19zY2FsZWRbLC0xXSA8LSBzY2FsZShzdW1zdGF0c19zY2FsZWRbLC0xXSkKCmxvX3NpZ25hbCA8LSBzdWJzZXQoc3Vtc3RhdHMsIG1lYW48MC4wNSAmIHNkPDAuMDEpCmxvX2lkcyA8LSBsb19zaWduYWwkcmVnX2lkCm1lc3NhZ2UocGFzdGUoIlRoZXJlIGFyZSIsbnJvdyhsb19zaWduYWwpLCJjZWxscyB3aXRoIHZlcnkgbG93IGFjdGl2aXR5IikpCmBgYAojIyMgQ2F0Y2gtMjIgdmFyaWFibGVzCkxvY2F0aW9uIG9mIGxvdy1zaWduYWwgdHJhY2VzIGluIENhdGNoLTIyIFBDQSBzcGFjZQpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwbG90KHBjX2NhdGNoW1sneCddXVssMV0sIHBjX2NhdGNoW1sneCddXVssMl0sIHBjaD0xOSwgY2V4PTAuNSwgeGxhYj0iUEMxIiwgeWxhYj0iUEMyIikKcG9pbnRzKHBjX2NhdGNoW1sneCddXVtsb19zaWduYWwkcmVnX2lkLDFdLCBwY19jYXRjaFtbJ3gnXV1bbG9fc2lnbmFsJHJlZ19pZCwyXSwgcGNoPTE5LCBjZXg9MC41LCBjb2w9InllbGxvdyIpCgpgYGAKIyMgQm91bmRhcmllcyBvZiBDYXRjaC0yMiBQQ0Egc3BhY2UKSURzIG9uIHRoZSBib3VuZGFyaWVzIChtYW51YWxseSBpZGVudGlmaWVkIGZyb20gYmlwbG90IGFib3ZlKQo2MDQsIDUyMiwgMjEwLCA4NzAsIDk0NiwgNTM5LCA1NDIsIDc0NCwgNjc1CgoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KZXBzX3ZhbCA8LSAyCmNsdXN0IDwtIGRic2Nhbjo6ZGJzY2FuKHBjX2NhdGNoW1sneCddXSwgZXBzPWVwc192YWwsIG1pblB0cyA9IDIwKQoKbXljb2xzIDwtIHJldihkaWNocm9tYXQ6OmNvbG9yc2NoZW1lcyRDYXRlZ29yaWNhbC4xMilbc2VxKGxlbmd0aCh1bmlxdWUoY2x1c3QkY2x1c3RlcikpKV0KCnBsb3QocGNfY2F0Y2hbWyd4J11dWywxXSwgcGNfY2F0Y2hbWyd4J11dWywyXSwgcGNoPTE5LCBjZXg9MC41LCAKICAgICBjb2w9bXljb2xzW2NsdXN0JGNsdXN0ZXIrMV0sCiAgICAgeGxhYj0iUEMxIiwgeWxhYj0iUEMyIikKbXRleHQocGFzdGUoImVwcz0iLGVwc192YWwsInByb2R1Y2VkIixsZW5ndGgodW5pcXVlKGNsdXN0JGNsdXN0ZXIpKSwiY2x1c3RlcnMiKSwgMykKbGVnZW5kKC0xMiw2LGxlZ2VuZD1wYXN0ZSgiY2x1c3QiLHNlcShsZW5ndGgodW5pcXVlKGNsdXN0JGNsdXN0ZXIpKSkpLCBjb2w9bXljb2xzLCBwY2g9MTksIGNleD0wLjUpCmBgYAoKYGBge3J9Cm1lc3NhZ2UoY2F0KCJXaXRoIGVwcz0iLGVwc192YWwsIiwiLGxlbmd0aCh1bmlxdWUoY2x1c3QkY2x1c3RlcikpLCAiY2x1c3RlcnMgd2VyZSBpZGVudGlmaWVkIikpCmRpcHJhdGU6Om5FYWNoKGNsdXN0JGNsdXN0ZXIpCmBgYAoKIyMjIE5vIHNwaWtlcyBsb29rcyBsaWtlIGNsdXN0ZXI9PTEKYGBge3J9CnNldC5zZWVkKDEwKQpjaGVja3RoZXNlIDwtIHNhbXBsZShyb2lzW2NsdXN0JGNsdXN0ZXI9PTFdLCAxNSkKcGxvdFRyYWNlRnVyYShjaGVja3RoZXNlLCBkKQpgYGAKIyMjIENvbXBhcmUgdG8gbG9faWRzCmBgYHtyfQpwbG90VHJhY2VGdXJhKGxvX2lkc1sxOjE1XSwgZCkKYGBgCmBgYHtyfQphbGxfbm9fc3Bpa2VfaWRzIDwtIHVuaW9uKHJvaXNbY2x1c3QkY2x1c3Rlcj09MV0sbG9faWRzKQpsZW5ndGgoYWxsX25vX3NwaWtlX2lkcykKYGBgCgojIyMgb3RoZXJzICh3aXRoIHNwaWtlcz8pIGNsdXN0ZXI9PTAKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0Kc2V0LnNlZWQoNikKY2hlY2t0aGVzZSA8LSBzYW1wbGUocm9pc1tjbHVzdCRjbHVzdGVyPT0wXSwgNjApCnBsb3RUcmFjZUZ1cmEoY2hlY2t0aGVzZVsxOjE1XSwgZCkKYGBgCgojIyMgQ3JpdGVyaWEKUGhpbGlwIGRlc2NyaWJlZCBhIGZldyBiYXNpYyB0eXBlcyBvZiB0cmFjZXMgaGUgc2VlczoKCjEpIG5vIHNwaWtlcyAgCjEpIHBlcmlvZGljIHNwaWtpbmcgKG9zY2lsbGF0b3J5KSAgCjEpIHNwb3JhZGljIGluZGl2aWR1YWwgc3Bpa2luZyAocmFyZSBhbmQgcmFuZG9tKSAgCjEpIHNwb3JhZGljIHN1c3RhaW5lZCBzcGlraW5nICAKMSkgc3VzdGFpbmVkIGFjdGl2YXRpb24gKHR3by1sZXZlbCB0cmFjZSkgIAoKCiMjIyBBbGdvcml0aG1zIGZvciBjbGFzc2lmaWNhdGlvbgoKRmFzdCBmb3VyaWVyIHRyYW5zZm9ybSAgdG8gZGV0ZWN0IHJlZ3VsYXIgb3NjaWxsYXRvcnkgcGF0dGVybnMKYGBge3J9CmYgPC0gbXZmZnQoYXMubWF0cml4KGZybikpCgpvc2NpbCA8LSBzYXBwbHkocm9pcywgZnVuY3Rpb24ocm9pKSBhbnkoUmUoZlssYXMuY2hhcmFjdGVyKHJvaSldKVszOihucm93KGYpLTMpXSA+IDEwKSkKcm9pX29zY2lsIDwtIHJvaXNbb3NjaWxdCmBgYAoKYGBge3J9CnBsb3Qoc2VxKDIsbnJvdyhmKS0xKSwgdGFpbChoZWFkKFJlKGZbLHJvaV9vc2NpbFsyXV0pLC0xKSwtMSksIHR5cGU9J2wnLCB5bGltPWMoLTEsMjApKQpgYGAKCiMjIyBFeGFtcGxlIG9zY2lsbGF0b3J5IHRyYWNlcwpgYGB7ciBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD02fQpwbG90VHJhY2VGdXJhKHJvaV9vc2NpbFsxMToyMF0sIGQpCmBgYAoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KcGxvdChwY19jYXRjaFtbJ3gnXV1bLDFdLCBwY19jYXRjaFtbJ3gnXV1bLDJdLCBwY2g9MTksIGNleD0wLjUsIAogICAgIGNvbD1jKCJibHVlIiwib3JhbmdlIilbKHN1bXN0YXRzJHJlZ19pZCAlaW4lIHJvaV9vc2NpbCkrMV0sCiAgICAgeGxhYj0iUEMxIiwgeWxhYj0iUEMyIikKCmBgYAoKIyMjIEV4YW1wbGUgdHJhY2VzIHdpdGggaGkgbWVhbiB2YWx1ZXMKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0KaGkgPC0gc3Vtc3RhdHNfc2NhbGVkW3N1bXN0YXRzX3NjYWxlZCRtZWFuID49MC4xICYgc3Vtc3RhdHNfc2NhbGVkJHNkIDw9IDAsInJlZ19pZCJdCgpwbG90VHJhY2VGdXJhKGhpWzE6MTBdLCBkKQpgYGAKCgojIFNpbmdsZSBzcGlrZXMKYGBge3J9CnNpbmdsZV9zcGlrZSA8LSBzdW1zdGF0c1tzdW1zdGF0cyRudW0uc3Bpa2VzPT0xLCJyZWdfaWQiXQpteWNvbHMzIDwtIHZpcmlkaXNMaXRlOjp2aXJpZGlzKG49MzgpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTZ9CnBsb3RUcmFjZUZ1cmEoc2luZ2xlX3NwaWtlWzE6MjBdLCBkKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTZ9CmhpX3Nsb3BlIDwtIHJvaXNbd2hpY2gobD40ZS01KV0KcGxvdFRyYWNlRnVyYShoaV9zbG9wZVsxOjEwXSwgZCkKCmBgYAoKYGBge3IgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9NH0KcGxvdChwY19jYXRjaFtbJ3gnXV1bLDFdLCBwY19jYXRjaFtbJ3gnXV1bLDJdLCBwY2g9MTksIGNleD0wLjUsIAogICAgIGNvbD1jKCJibHVlIiwib3JhbmdlIilbKHN1bXN0YXRzJHJlZ19pZCAlaW4lIGhpX3Nsb3BlKSsxXSwKICAgICB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiKQpgYGAKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CnBsb3QocGNfY2F0Y2hbWyd4J11dWywxXSwgcGNfY2F0Y2hbWyd4J11dWywyXSwgcGNoPTE5LCBjZXg9MC41LCB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiKQpwb2ludHMocGNfY2F0Y2hbWyd4J11dW3NpbmdsZV9zcGlrZSwxXSwgcGNfY2F0Y2hbWyd4J11dW3NpbmdsZV9zcGlrZSwyXSwgcGNoPTE5LCBjZXg9MC41LCBjb2w9InllbGxvdyIpCgpgYGAKIyMjIEV4cHQgY29uZGl0aW9ucwpgYGB7ciBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwbG90KHBjX2NhdGNoW1sneCddXVssMV0sIHBjX2NhdGNoW1sneCddXVssMl0sIHBjaD0iLiIsIGNleD0wLjUsIAogICAgIGNvbD1yZXYoZGljaHJvbWF0Ojpjb2xvcnNjaGVtZXMkQ2F0ZWdvcmljYWwuMTIpW21hdGNoKGdzdWIoIl9bMC05XXsxLDN9JCIsICIiLCBzdW1zdGF0cyRyZWdfaWQpLGNvbmRpdGlvbnMpXSwKICAgICB4bGFiPSJQQzEiLCB5bGFiPSJQQzIiKQpsZWdlbmQoLTEzLDgsIGNvbmRpdGlvbnMsIGNvbD1kaWNocm9tYXQ6OmNvbG9yc2NoZW1lcyRDYXRlZ29yaWNhbC4xMltzZXFfYWxvbmcoY29uZGl0aW9ucyldLCBjZXg9MC41LCBwY2g9MTkpCmBgYAoKCmBgYHtyIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CnNldC5zZWVkKDEzKQpteV9zYW1wbGVzIDwtIGludmlzaWJsZShsYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY29uZCkgewogICAgYSA8LSBzYW1wbGVST0lzKGNvbmRpdGlvbj1jb25kKQogICAgcGxvdFBDY2F0Y2goYSwgY2x1c3QkY2x1c3RlciwgbWFpbj1jb25kKQogICAgYQp9KSkKYGBgCgoKYGBge3J9CnNldC5zZWVkKDEzKQpteV9zYW1wbGVzIDwtIGludmlzaWJsZShsYXBwbHkoY29uZGl0aW9ucywgZnVuY3Rpb24oY29uZCkgewogICAgYSA8LSBzYW1wbGVST0lzKGNvbmRpdGlvbj1jb25kKQogICAgcGxvdFBDY2F0Y2goYSwgYXMuaW50ZWdlcighcm9pcyAlaW4lIGFsbF9ub19zcGlrZV9pZHMpLCBtYWluPWNvbmQpCiAgICBhCn0pKQpgYGAKCgoKYGBge3IgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9Nn0KbGFwcGx5KHNlcV9hbG9uZyhteV9zYW1wbGVzKSwgZnVuY3Rpb24oaSkgcGxvdFRyYWNlRnVyYShteV9zYW1wbGVzW1tpXV1bIW15X3NhbXBsZXNbW2ldXSAlaW4lIGFsbF9ub19zcGlrZV9pZHNdWzE6MjBdLCBkKSkKYGBgCgojIyMgRXhhbXBsZSB0cmFjZXMKRmlndXJlIDFCCmBgYHtyIGZpZy5oZWlnaHQ9My41LCBmaWcud2lkdGg9NX0KZXhlbXBsYXJfaWRzIDwtIGMoICAiMjAyMTExMTRfMTRkYXk4dU1QTFhfMTA1IiwKICAgICAgICAgICAgICAgICAgICAiMjAyMTExMTRfMTRkYXk4dU1QTFhfODAyIiwKICAgICAgICAgICAgICAgICAgICAiMjAyMTExMTRfMTRkYXk4dU1QTFhfMjk3IiwKICAgICAgICAgICAgICAgICAgICAiMjAyMjAzMjFfN2RheTh1TVBMWF8zNDAiLAogICAgICAgICAgICAgICAgICAgICIyMDIyMDMyMV83ZGF5OHVNUExYXzExMjEiLAogICAgICAgICAgICAgICAgICAgICIyMDIxMTExNF8xNGRheTh1TVBMWF82MDQiLAogICAgICAgICAgICAgICAgICAgICIyMDIxMTExNF8xNGRheTh1TVBMWF8yMCIKKQoKc3Bpa2VfZXhhbXBsZXMgPC0gZFtkJHJlZ19pZCAlaW4lIGV4ZW1wbGFyX2lkcywgXQpzcGlrZV9leGFtcGxlcyRyZWdfaWQgPC0gZmFjdG9yKHNwaWtlX2V4YW1wbGVzJHJlZ19pZCwgbGV2ZWxzPWV4ZW1wbGFyX2lkcykKCnBsb3RUcmFjZUZ1cmEoZXhlbXBsYXJfaWRzLCBzcGlrZV9leGFtcGxlcywgeWw9YygwLjUsMS4yNSkpCgojIGdnc2F2ZSgiZXhhbXBsZV9mdXJhMl90cmFjZXMucG5nIiwgZGV2aWNlPSJwbmciLCB3aWR0aD01LCBoZWlnaHQ9My41LCB1bml0cz0iaW4iKQpgYGAKCgojIyMgUGxvdCAlIGluIGVhY2ggY2x1c3RlciBmb3IgZWFjaCBjb25kaXRpb24KUGVyZm9ybSBib290c3RyYXBwaW5nIHRvIGdldCBzYW1wbGUgc3RhdGlzdGljcy4gQXNzZXNzaW5nIHByb3BvcnRpb25zIG9mIGNlbGxzIGluIGNsdXN0ZXJzIDAgYW5kIDEgCgpgYGB7cn0KIyBmaWx0ZXJlZCBvYmplY3RzIGluIG5haXZlIGRhdGEgKHNob3VsZCBmaWx0ZXIgYWxsIGRhdGFzZXRzKSB3aXRoIGFyZWEgPiAxMDAgwrVNXjIgCgpzZXQuc2VlZCgxMykKCnByb3Bfc3Bpa2luZyA8LSBkby5jYWxsKGMsIGxhcHBseShjb25kaXRpb25zLCBmdW5jdGlvbihjbykgewogICAgc2FtcCA8LSBsYXBwbHkoMToxMDAsIGZ1bmN0aW9uKGkpIHNhbXBsZShyb2lzW2dyZXBsKGNvLHJvaXMpXSwyMDAsIHJlcGxhY2U9VFJVRSkpCiAgICBzYXBwbHkoc2FtcCwgZnVuY3Rpb24oeCkgbGVuZ3RoKHdoaWNoKCF4ICVpbiUgYWxsX25vX3NwaWtlX2lkcykpLzIwMCkKfSkpCgpwcm9wX3NwaWtpbmdfZGYgPC0gZGF0YS5mcmFtZShjb25kaXRpb249cmVwKGNvbmRpdGlvbnMsIGVhY2g9MTAwKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3Bfc3Bpa2luZz1wcm9wX3NwaWtpbmcpCgpwcm9wX3NwaWtpbmdfY29uZF9kZiA8LSBwcm9wX3NwaWtpbmdfZGYKcHJvcF9zcGlraW5nX2NvbmRfZGYkZGF0ZSA8LSAgc2FwcGx5KHN0cnNwbGl0KHByb3Bfc3Bpa2luZ19jb25kX2RmJGNvbmRpdGlvbiwgIl8iKSwgIltbIiwgMSkKcHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uIDwtICBzYXBwbHkoc3Ryc3BsaXQocHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uLCAiXyIpLCAiW1siLCAyKQpwcm9wX3NwaWtpbmdfY29uZF9kZiRjb25kaXRpb24gPC0gIGdzdWIoImRheTh1TVBMWCIsIiIscHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uKQpwcm9wX3NwaWtpbmdfY29uZF9kZltncmVwKCJuYWl2ZSIscHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uKSwiY29uZGl0aW9uIl0gPC0gIDAKcHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uIDwtICBmYWN0b3IocHJvcF9zcGlraW5nX2NvbmRfZGYkY29uZGl0aW9uLCBsZXZlbHM9YygiMCIsICI1IiwiNyIsIjEwIiwiMTQiKSkKcHJvcF9zcGlraW5nX2NvbmRfZGYgPC0gcHJvcF9zcGlraW5nX2NvbmRfZGZbcHJvcF9zcGlraW5nX2NvbmRfZGYkZGF0ZSAhPSAiMjAyMzA0MjYiLF0KYGBgCgojIyMgVGltZSBkZXBlbmRlbmNlIG9mIHNwaWtpbmcgYmVoYXZpb3IKRmlndXJlIDFDCmBgYHtyIFZpb2xpbiBwbG90LCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD00fQpwIDwtIGdncGxvdChkYXRhPXByb3Bfc3Bpa2luZ19jb25kX2RmLCBhZXMoeD1jb25kaXRpb24sIHk9cHJvcF9zcGlraW5nLCBmaWxsPTEpKSArIAogICAgZ2VvbV92aW9saW4od2lkdGg9Ljc1KSArIGdlb21fYm94cGxvdCh3aWR0aD0wLjEsIGNvbG9yPSJncmV5IiwgYWxwaGE9MC41KSArIAogICAgIyBjb29yZF9mbGlwKCkgKyAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIAogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xNiksCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2KSwKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplPTE4LCBmYWNlPSJib2xkIiksCiAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCwgZmFjZT0iYm9sZCIpKSArIAogICAgeWxpbShjKDAsMC43NSkpICsKICAgIHlsYWIoIlByb3BvcnRpb24gc3Bpa2luZyIpICsgCiAgICB4bGFiKCJEYXlzIGluIEJSQUZpIikKcAoKIyBnZ3NhdmUoIkJSQUZpX3RpbWVfcHJvcF9zcGlraW5nLnBuZyIsIGRldmljZT0icG5nIiwgd2lkdGg9NCwgaGVpZ2h0PTQsIHVuaXRzPSJpbiIpCmBgYAoKCmBgYHtyfQptIDwtIGxtKHByb3Bfc3Bpa2luZyB+IGNvbmRpdGlvbiwgZGF0YT1wcm9wX3NwaWtpbmdfY29uZF9kZikKYSA8LSBhb3YobSkKdHVrZXkgPC0gVHVrZXlIU0QoYSkKdHVrZXkkY29uZGl0aW9uCmBgYAoKCiMjIyBOdW1iZXIgb2YgY2VsbHMgcGVyIGNvbmRpdGlvbgpgYGB7cn0Kc2FwcGx5KGNvbmRpdGlvbnMsIGZ1bmN0aW9uKGNvKSBsZW5ndGgodW5pcXVlKHJvaXNbZ3JlcChjbyxyb2lzKV0pKSkKYGBgCgoKCiMgQW5hbHlzaXMgb2YgZXh0cmFjZWxsdWxhciBjYWxjaXVtIG9uIGxvbmctdGVybSBCUkFGaS10cmVhdGVkIGNlbGwgc3Bpa2luZyBiZWhhdmlvcgojIyBTdXBwbGVtZW50YXJ5IEZpZ3VyZSBTNAojIyMgQW5hbHlzaXMgb2YgbWFudWFsIHF1YW50aWZpY2F0aW9uIG9mIDggZGF5IEJSQUZpIHcvbyBjYWxjaXVtClBlcmZvcm1lZCBieSBQaGlsaXAuIFdpbGwgYXBwZW5kIG1hbnVhbGx5IGFzc2Vzc2VkIG5vLXNwaWtlIGByZWdfaWRgcyB0byBgYWxsX25vX3NwaWtlX2lkc2AuCgpgYGB7cn0Kbm9DYV84ZF9tYW51YWwgPC0gcmVhZC5jc3YoIi4uL2RhdGEvMjAyMzA0MjZfOGRCUkFGaWNhMmZyZWVfc2FtcGxlX2lkcy5jc3YiKQp0ZXN0cm9pcyA8LSBjKG5vQ2FfOGRfbWFudWFsJHJlZ19pZCwgcm9pc1tncmVwbCgiMjAyMjAzMjFfN2RheTh1TVBMWCIscm9pcyldKQpgYGAKCgpgYGB7cn0Kc2V0LnNlZWQoMTMpCnRlc3Rjb25kcyA8LSBjKCIyMDIyMDMyMV83ZGF5OHVNUExYIiwiMjAyMzA0MjZfOGRheTh1TVBMWENhMmZyZWUiKQoKcHJvcF9zcGlraW5nX2NhIDwtIGRvLmNhbGwoYywgbGFwcGx5KHRlc3Rjb25kcywgZnVuY3Rpb24oY28pIHsKICAgIHNhbXAgPC0gbGFwcGx5KDE6MTAwLCBmdW5jdGlvbihpKSBzYW1wbGUodGVzdHJvaXNbZ3JlcGwoY28sdGVzdHJvaXMpXSwgMjAwLCByZXBsYWNlID0gVFJVRSkpCiAgICBzYXBwbHkoc2FtcCwgZnVuY3Rpb24oeCkgbGVuZ3RoKHdoaWNoKCF4ICVpbiUgYyhhbGxfbm9fc3Bpa2VfaWRzLG5vQ2FfOGRfbWFudWFsJHJlZ19pZFtub0NhXzhkX21hbnVhbCRzcGlrZXM9PTBdKSkpLzIwMCkKfSkpCgpwcm9wX3NwaWtpbmdfY2FfZGYgPC0gZGF0YS5mcmFtZShjb25kaXRpb249cmVwKGMoIkNhMisiLCJubyBDYTIrIiksIGVhY2g9MTAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcF9zcGlraW5nPXByb3Bfc3Bpa2luZ19jYSkKCmBgYAoKCiMjIyA4IGRheXMgQlJBRmkgdy9vIENhMisgaW4gYnVmZmVyIHZzIDcgZGF5cyBCUkFGaSB3LyBDYTIrIGluIGJ1ZmZlcgpTdXBwbGVtZW50YXJ5IEZpZ3VyZSBTNApgYGB7ciBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD0yfQpwIDwtIGdncGxvdChkYXRhPXByb3Bfc3Bpa2luZ19jYV9kZiwgYWVzKHg9Y29uZGl0aW9uLCB5PXByb3Bfc3Bpa2luZywgZmlsbD0xKSkgKyAKICAgIGdlb21fdmlvbGluKHdpZHRoPS43NSkgKyAKICAgIGdlb21fYm94cGxvdCh3aWR0aD0wLjEsIGNvbG9yPSJncmV5IiwgYWxwaGE9MC41KSArIAogICAgIyBjb29yZF9mbGlwKCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplPTEyKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemU9MTIpLAogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemU9MTYsIGZhY2U9ImJvbGQiKSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplPTE2LCBmYWNlPSJib2xkIikpICsgCiAgICAjIHlsaW0oYygwLDAuNzUpKSArCiAgICB5bGFiKCJQcm9wb3J0aW9uIHNwaWtpbmciKSArIAogICAgeGxhYigiIikgCnAKYGBgCgo=